﻿using System;
using System.Net;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.Diagnostics;
using System.Collections.Generic;

namespace Airport
{
    [DataContract]
    [KnownType(typeof(WorldVector))]
    public class Plane : IPlane, IEquatable<Plane>
    {
        private int _planeId;
        private string _name;
        private string _tailNumber;
        private int _heading;
        private double _airspeed;
        private double _groundSpeed;
        private int _altitude;
        private int _bufferZone;
        private PlaneType _planeType;
        private Route _currentRoute;
        private Node _destinationNode;
        private string _destinationGate;
        private Node _location;
        private Node _previousMapNode;
        private int _previousRouteNodeIndex;
        private Edge _currentEdge;
        private int _nextRouteNodeIndex;
        private PlaneStatus _planeStatus;
        private PlaneUpdateType _updateType;
        private Flight _planeFlight;
        private bool _isSelected;

        // EnvironmentUpdater-related variables
        private float _mass; // kg
        private WorldVector _positionVector; // m
        private WorldVector _velocityVector; // m/s
        private WorldVector _accelerationVector; // m/s^2
        private WorldVector _forceVector; // N (kg * m/s^2)
        private float _takeoffThrust;
        private float _reverseThrust;
        private float _takeoffSpeed;
        private DateTime _lastUpdate;

        public Plane()
            : this("")
        {
        }

        public Plane(string name)
        {
            _planeId = -1;
            _tailNumber = "";
            _heading = -1;
            _airspeed = -1;
            _groundSpeed = -1;
            _altitude = -2147483648;
            _bufferZone = -1;
            _planeType = new PlaneType();
            _currentRoute = new Route();
            _location = new Node();
            _currentEdge = new Edge();
            _planeStatus = PlaneStatus.Uninitialized;
            _destinationGate = "";
            _isSelected = false;

            // Initialize EnvironmentUpdater Variables
            _name = name;
            _mass = 335000; // minimum weight of Boeing 747
            _takeoffThrust = 276000; // maximum thrust of Boeing 747-400
            _takeoffSpeed = 80;
            _reverseThrust = -276000; 
            _positionVector = new WorldVector();
            _velocityVector = new WorldVector();
            _accelerationVector = new WorldVector();
            _forceVector = new WorldVector();
            _nextRouteNodeIndex = -1;
            _previousRouteNodeIndex = -1;
        }         
        /*
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        */
        [DataMember]
        public int PlaneId
        {
            get
            {
                return _planeId;
            }

            set
            {
                if (_planeId != value)
                {
                    _planeId = value;
                }
            }
        }

        [DataMember]
        public string TailNumber
        {
            get
            {
                return _tailNumber;
            }

            set
            {
                if (_tailNumber != value)
                {
                    _tailNumber = value;
                }
            }
        }

        [DataMember]
        public int Heading
        {
            get
            {
                return _heading;
            }

            set
            {
                if (_heading != value)
                {
                    _heading = value;
                }
            }
        }

        //[DataMember]
        public double Airspeed
        {
            get
            {
                return _airspeed;
            }

            set
            {
                if (_airspeed != value)
                {
                    _airspeed = value;
                }
            }
        }

        //[DataMember]
        public double GroundSpeed
        {
            get
            {
                return _groundSpeed;
            }

            set
            {
                if (_groundSpeed != value)
                {
                    _groundSpeed = value;
                }
            }
        }

        //[DataMember]
        public int Altitude
        {
            get
            {
                return _altitude;
            }

            set
            {
                if (_altitude != value)
                {
                    _altitude = value;
                }
            }
        }

        //[DataMember]
        public int BufferZone
        {
            get
            {
                return _bufferZone;
            }

            set
            {
                if (_bufferZone != value)
                {
                    _bufferZone = value;
                }
            }
        }

        //[DataMember]
        public PlaneType Type
        {
            get
            {
                return _planeType;
            }

            set
            {
                if (_planeType != value)
                {
                    _planeType = value;
                }
            }
        }

        [DataMember]
        public Route CurrentRoute
        {
            get
            {
                return _currentRoute;
            }

            set
            {
                if (_currentRoute != value)
                {
                    _currentRoute = value;
                }
            }
        }

        [DataMember]
        public Node DestinationNode
        {
            get
            {
                return _destinationNode;
            }

            set
            {
                if (_destinationNode != value)
                {
                    _destinationNode = value;
                }
            }
        }

        [DataMember]
        public string DestinationGate
        {
            get
            {
                return _destinationGate;
            }

            set
            {
                _destinationGate = value;
            }
        }

        /// <summary>
        /// The Node on which the Plane is currently located.
        /// </summary>
        [DataMember]
        public Node Location
        {
            get
            {
                return _location;
            }

            set
            {
                if (_location != value)
                {
                    _location = value;
                    
                }
            }
        }

        /// <summary>
        /// The last Node that the Plane crossed.
        /// </summary>
        //[DataMember]
        public Node PreviousMapNode
        {
            get
            {
                return _previousMapNode;
            }

            set
            {
                _previousMapNode = value;
            }
        }

        [DataMember]
        public bool IsSelected
        {
            get
            {
                return _isSelected;
            }

            set
            {
                _isSelected = value;
            }
        }

        public int PreviousRouteNodeIndex
        {
            get
            {
                return _previousRouteNodeIndex;
            }

            set
            {
                if (_previousRouteNodeIndex != value)
                {
                    _previousRouteNodeIndex = value;

                }
            }
        }

        /// <summary>
        /// The Edge on which the Plane is currently located.
        /// </summary>
        [DataMember]
        public Edge CurrentEdge
        {
            get
            {
                return _currentEdge;
            }

            set
            {
                if (value != _currentEdge)
                {
                    if (_previousMapNode != null && _currentEdge != null)
                    {
                        // If _previousMapNode was an endpoint of _currentEdge, the Plane
                        // just passed the other endpoint
                        if (_previousMapNode == _currentEdge.Endpoint1)
                        {
                            _previousMapNode = _currentEdge.Endpoint2;
                        }
                        else
                        {
                            _previousMapNode = _currentEdge.Endpoint1;
                        }
                    }
                    _currentEdge = value;
                }
            }
        }

        [DataMember]
        public int NextRouteNodeIndex
        {
            get
            {
                return _nextRouteNodeIndex;
            }

            set
            {
                if (_nextRouteNodeIndex != value)
                {                    
                    _nextRouteNodeIndex = value;
                }
            }
        }

        [DataMember]
        public PlaneStatus Status
        {
            get
            {
                return _planeStatus;
            }

            set
            {
                if (_planeStatus != value)
                {
                    _planeStatus = value;
                }
            }
        }

        [DataMember]
        public PlaneUpdateType UpdateType
        {
            get
            {
                return _updateType;
            }

            set
            {
                if (_updateType != value)
                {
                    _updateType = value;
                }
            }
        }

        [DataMember]
        public Flight PlaneFlight
        {
            get
            {
                return _planeFlight;
            }

            set
            {
                _planeFlight = value;
            }
        }

        [DataMember]
        public WorldVector PositionVector
        {
            get
            {
                return _positionVector;
            }

            set
            {                
                if (value != null && _currentRoute != null && _currentRoute.RouteNodes.Count > 0)
                {
                    if (_positionVector != null)
                    {
                        string newPositionVectorString = "Plane: Updating PositionVector\n";

                        float updatedDifferenceX, updatedDifferenceY;
                        float nextRouteNodeDifferenceX, nextRouteNodeDifferenceY;

                        do
                        {
                            updatedDifferenceX = Math.Abs(_positionVector.X - value.X);
                            updatedDifferenceY = Math.Abs(_positionVector.Y - value.Y);
                            nextRouteNodeDifferenceX = Math.Abs(_positionVector.X - _currentRoute.RouteNodes[_nextRouteNodeIndex].XPos);
                            nextRouteNodeDifferenceY = Math.Abs(_positionVector.Y - _currentRoute.RouteNodes[_nextRouteNodeIndex].YPos);

                            newPositionVectorString += "Old PositionVector: " + _positionVector.ToString() + "\n";
                            newPositionVectorString += "New PositionVector: " + value.ToString() + "\n";
                            newPositionVectorString += "Next RouteNode: " + _currentRoute.RouteNodes[_nextRouteNodeIndex].ToString() + "\n";
                            newPositionVectorString += "updatedDifferenceX: " + updatedDifferenceX + "\n";
                            newPositionVectorString += "updatedDifferenceY: " + updatedDifferenceY + "\n";
                            newPositionVectorString += "nextRouteNodeDifferenceX: " + nextRouteNodeDifferenceX + "\n";
                            newPositionVectorString += "nextRouteNodeDifferenceY: " + nextRouteNodeDifferenceY + "\n";

                            Debug.WriteLine(newPositionVectorString);
                            // If the plane is at or has overshot the next RouteNode, increment _nextRouteNodeIndex
                            if ((updatedDifferenceX >= nextRouteNodeDifferenceX) || (updatedDifferenceY >= nextRouteNodeDifferenceY))
                            {
                                _nextRouteNodeIndex++;
                            }
                        }
                        while ((_nextRouteNodeIndex < _currentRoute.RouteNodes.Count) 
                            && ((updatedDifferenceX >= nextRouteNodeDifferenceX) 
                                    || (updatedDifferenceY >= nextRouteNodeDifferenceY)));

                    }
                    // Set the _currentRoute's StartPoint to the updated position
                    _currentRoute.StartPoint = new Point((int)value.X, (int)value.Y);
                }

                _positionVector = value;
                
            }
        }

        [DataMember]
        public WorldVector VelocityVector
        {
            get
            {
                return _velocityVector;
            }

            set 
            { 
                _velocityVector = value; 
            }
        }

        public WorldVector AccelerationVector
        {
            get
            {
                return _accelerationVector;
            }

            set 
            { 
                _accelerationVector = value; 
            }
        }

        public WorldVector ForceVector
        {
            get
            {
                return _forceVector;
            }

            set
            {
                _forceVector = value;
            }
        }

        public float TakeoffThrust
        {
            get
            {
                return _takeoffThrust;
            }

            set
            {
                _takeoffThrust = value;
            }
        }

        public float TakeoffSpeed
        {
            get
            {
                return _takeoffSpeed;
            }

            set
            {
                _takeoffSpeed = value;
            }
        }

        public float ReverseThrust
        {
            get
            {
                return _reverseThrust;
            }

            set
            {
                _reverseThrust = value;
            }
        }

        public float Mass
        {
            get
            {
                return _mass;
            }

            set
            {
                _mass = value;
            }
        }

        public DateTime LastUpdate
        {
            get
            {
                return _lastUpdate;
            }

            set
            {
                _lastUpdate = value;
            }
        }

        [DataMember]
        public string Name
        {
            get
            {
                return _name;
            }

            set
            {
                _name = value;
            }
        }

        public void SetTakingOff()
        {
            // adjust force accordingly
            _forceVector.Z = 100000;
        }

        public void SetLanding()
        {
            // adjust force accordingly
            _forceVector.Z = -100000;
        }        

        public override bool Equals(Object obj)
        {
            // TODO Check more than just planeId

            if (obj == null)
            {
                return false;
            }

            if (obj.GetType() != typeof(Plane))
            {
                return false;
            }

            if (this._planeId != ((Plane)obj)._planeId)
            {
                return false;
            }

            return true;
        }

        public bool Equals(Plane obj)
        {
            // TODO Check more than just planeId

            if (obj == null)
            {
                return false;
            }            

            if (this._planeId != ((Plane)obj)._planeId)
            {
                return false;
            }

            return true;
        }

        public override int GetHashCode()
        {
            return _planeId;
        }

        public static bool operator ==(Plane a, Plane b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object)a == null) || ((object)b == null))
            {
                return false;
            }

            return a._planeId == b._planeId;
        }

        public static bool operator !=(Plane a, Plane b)
        {
            return !(a == b);
        }

        public override string ToString()
        {
            return "PlaneID: " + _planeId;
        }
    }

    public class PlaneComparer : Comparer<Plane>
    {
        public override int Compare(Plane x, Plane y)
        {
            if (x.PlaneId < y.PlaneId)
            {
                return -1;
            }

            if (x.PlaneId == y.PlaneId)
            {
                return 0;
            }

            // x.PlaneId > y.PlaneId
            return 1;
        }
    }

    [DataContract]
    // This may be unneccessary
    public enum PlaneStatus
    {
        [EnumMember]
        Uninitialized,
        [EnumMember]
        Holding,
        [EnumMember]
        Taxiing,
        [EnumMember]
        ClearedForTakeoff,
        [EnumMember]
        TakingOff,
        [EnumMember]
        ClearedForLanding,
        [EnumMember]
        Landing,
        [EnumMember]
        Towing,
        [EnumMember]
        LinedUpAndWaiting,
        [EnumMember]
        Stopped,
        [EnumMember]
        Crashed,
        [EnumMember]
        ClearedToCrossRunway
    }

    [DataContract]
    public enum PlaneUpdateType
    {
        [EnumMember]
        AddNewPlane,
        [EnumMember]
        UpdateExistingPlane,
        [EnumMember]
        DeletePlane
    }
}
